export class Realm {
  constructor() {
    this.global = new Map()
    this.Object = new Map()
    this.Object.call = function() {

    }
    this.Object_prototype = new Map()
  }
}

export class EnvironmentRecord {
  constructor() {
    this.thisValue
    this.varables = new Map()
    this.outer = null
  }
}

export class ExecutionContext {
  constructor(realm, lexicalEnvironment, variableEnvironment) {
    variableEnvironment = variableEnvironment || lexicalEnvironment
    this.lexicalEnvironment = lexicalEnvironment
    this.variableEnvironment = variableEnvironment
    this.realm = realm
  }
}

export class Reference {
  constructor(object, property) {
    this.object = object
    this.property = property
  }
  set(val) {
    this.object[this.property] = val
  }
  get() {
    return this.object[this.property]
  }
}

export class JSValue {
  get type() {
    if (this.constructor === JSNumber) {
      return 'number'
    }
    if (this.constructor === JSString) {
      return 'string'
    }
    if (this.constructor === JSBoolean) {
      return 'boolean'
    }
    if (this.constructor === JSObject) {
      return 'object'
    }
    if (this.constructor === JSNull) {
      return 'null'
    }
    if (this.constructor === JSSymbol) {
      return 'symbol'
    }
    return 'undefined'
  }
}

export class JSNumber extends JSValue {
  constructor(value) {
    super()
    this.memory = new ArrayBuffer(8)
    if (arguments.length) {
      new Float64Array(this.memory)[0] = value
    } else {
      new Float64Array(this.memory)[0] = 0
    }
  }
  get value() {
    return new Float64Array(this.memory)[0]
  }
  toString() {

  }
  toNumber() {
    return this
  }
  toBoolean() {
    return new JSBoolean(new Float64Array(this.memory)[0] !== 0)
  }
}

export class JSString extends JSValue {
  constructor(characters) {
    super()
    // this.memory = new ArrayBuffer(characters.length)
    this.characters = characters
  }
  toString() {
    return this
  }
  toNumber() {
  }
  toBoolean() {
    return new Boolean(new Float64Array(this.memory)[0] !== 0)
  }
}

export class JSBoolean extends JSValue {
  constructor(value) {
    super()
    this.value = value || false
  }
  toString() {
    const chers = this.value ? ['t', 'r', 'u', 'e'] : ['f', 'a', 'l', 's', 'e']
    return new String(chers)
  }
  toNumber() {
    const num = this.value ? 1 : 0
    return new JSNumber(num)
  }
  toBoolean() {
    return this
  }
}

export class JSObject extends JSValue {
  constructor(proto) {
    super()
    this.properties = new Map()
    this.prototype = proto || null
  }
  setProperty(name, attributes) {
    this.properties.set(name, attributes)
  }
  getProperty() {
    // TODO
  }
  setPrototype(proto) {
    this.prototype = proto
  }
  getPrototype() {
    return this.prototype
  }
}

export class JSNull extends JSValue {
  toString() {
    return new String(['n', 'u', 'l', 'l'])
  }
  toNumber() {
    return new JSNumber(0)
  }
  toBoolean() {
    return new JSBoolean(false)
  }
}

export class JSUndefined extends JSValue {
  toString() {
    return new String(['u', 'n', 'd', 'e', 'f', 'i', 'n', 'e', 'd'])
  }
  toNumber() {
    return new JSNumber(0)
  }
  toBoolean() {
    return new JSBoolean(false)
  }
}

export class JSSymbol extends JSValue {
  constructor(name) {
    super()
    this.name = name || ''
  }
}





